
'''
 * Base class for representation of XSD duration, time, date/time
 * and related datatype instances. We are using the Xerces internal
 * packages for the all heavy lifting which represent date/times
 * using an int array. These wrapper classes just provide more
 * convenient access to the date values.
 * <p>
 * This class includes code derived from Xerces 2.6.0
 * Copyright (c) 1999-2002 The Apache Software Foundation.
 * All rights reserved.
 * </p>
'''
# implements Comparable<AbstractDateTime>:
import math
import copy
from base.java.Arrays import Arrays


#define constants
# int

CY = 0
M = 1
D = 2
h = 3
m = 4
s = 5
ms = 6
utc = 7
msscale = 8

# Timezone constants
# int
hh = 0
mm = 1


class AbstractDateTime:

    '''
     * The array of year/month etc values as ints
    '''
    # param
    # return
    # protected
    data = []

    '''
     * The fractional seconds
    '''
    # param
    # return
    # protected
    # double
    fractionalSeconds = 0.0

    #size for all objects must have the same fields:
    #CCYY, MM, DD, h, m, s, ms + timeZone
    # int 
    TOTAL_SIZE = 9

    # The number of comparable values
    # int 
    COMPARABLE_SUBSET = 6

    # 以下4个为short
    '''
     * constant to indicate a less than relationship from compare()
    '''
    LESS_THAN = -1
    '''
     * constant to indicate an equals relationship from compare()
    '''
    EQUAL = 0
    '''
     * constant to indicate a greater than relationship from compare()
    '''
    GREATER_THAN = 1
    '''
     * constant to indicate an indeterminate relationship from compare()
    '''
    INDETERMINATE = 2

    '''
     * Constructor
     *
     * @param value the date/time value returned by the parsing
    '''
    # param Object
    def __init__(self, value):
        # value 转 int[]
        self.data = value
        self.extractFractionalSeconds()
    

    '''
     * Comparison function. Not quite the same as normal java compare
     * because XSD date/times are not always comparable.
     *
     * @param other the time/date to compare to
     * @return an order flag - one of LESS_THAN, EQUAL, GREATER_THEN, INDETERMINATE
    '''
    # param AbstractDateTime
    # return int
    def compare(self, other):
        return self.compareValues(self.data, other.data, True)
    

    '''
     * Normal java comparison function. Treats INDETERMINATE as the same
     * as equals. This is not strictly correct but seems like an appropriate
     * way to handle partially ordered objects.
    '''
    # @Override
    # param AbstractDateTime
    # return int
    def compareTo(self, o):
        com = self.compare(o)
        if com == self.EQUAL or com == self.INDETERMINATE:
            return 0
        elif com == self.LESS_THAN:
            return -1
        elif com == self.GREATER_THAN:
            return 1
        return 0
    

    '''
     * Convert fractional second representation to a simple double..
    '''
    # param
    # return
    # protected
    def extractFractionalSeconds(self):
        fractionalSeconds = 0.0
        #global ms
        #global msscale
        if self.data[ms] != 0:
            # int
            fs = self.data[ms]
            fractionalSeconds = (fs) / math.pow(10.0, self.data[msscale])
        
    

    '''
     * Equality function
    '''
    # @Override
    # param Object
    # return boolean
    def equals(self, obj):
        if isinstance(obj, AbstractDateTime):
            # AbstractDateTime
            adt = obj
            for i in range(0, self.data.length):
                if (self.data[i] != adt.data[i]):
                    return False
            
            # fractionalSeconds is generated from data[ms]
            return True
        
        return False
    

    # @Override
    # param
    # return int
    def hashCode(self):
        # See JENA-1140.
        # Eclipse generated - without fractionalSeconds which is generated from data[ms]
        # final int
        prime = 31
        # int
        result = 1
        result = prime * result + Arrays.hashCode(self.data)
        return result
    

#  --------------------------------------------------------------------
#  This code is adapated from Xerces 2.6.0 AbstractDateTimeDV.    
#  Copyright (c) 1999-2003 The Apache Software Foundation.  All rights
#  reserved.
#  --------------------------------------------------------------------

    '''
     * Compare algorithm described in dateDime (3.2.7).
     *
     * @param date1  normalized date representation of the first value
     * @param date2  normalized date representation of the second value
     * @param strict
     * @return less, greater, less_equal, greater_equal, equal
    '''
    # param int[] int[] boolean
    # return short
    # protected
    def compareValues(self, date1, date2, strict):
        if (date1[utc] == date2[utc]):
            return self.compareOrder(date1, date2)
        
        # short 
        c1 = None
        c2 = None

        # 两个都是 int[] 
        # new int[TOTAL_SIZE]
        # new int[2]
        tempDate = []
        timeZone = []

        if (date1[utc] == 'Z'):
            #compare date1<=date1<=(date2 with time zone -14)
            #
            self.cloneDate(date2, tempDate) #clones date1 value to global temporary storage: fTempDate
            timeZone[hh] = 14
            timeZone[mm] = 0
            tempDate[utc] = '+'
            self.normalize(tempDate, timeZone)
            c1 = self.compareOrder(date1, tempDate)
            if (c1 == self.LESS_THAN):
                return c1

            #compare date1>=(date2 with time zone +14)
            #
            self.cloneDate(date2, tempDate) #clones date1 value to global temporary storage: tempDate
            timeZone[hh] = 14
            timeZone[mm] = 0
            tempDate[utc] = '-'
            self.normalize(tempDate, timeZone)
            c2 = self.compareOrder(date1, tempDate)
            if (c2 == self.GREATER_THAN):
                return c2

            return self.INDETERMINATE
        elif (date2[utc] == 'Z'):

            #compare (date1 with time zone -14)<=date2
            #
            self.cloneDate(date1, tempDate) #clones date1 value to global temporary storage: tempDate
            timeZone[hh] = 14
            timeZone[mm] = 0
            tempDate[utc] = '-'
            self.normalize(tempDate, timeZone)
            c1 = self.compareOrder(tempDate, date2)
            if (c1 == self.LESS_THAN):
                return c1

            #compare (date1 with time zone +14)<=date2
            #
            self.cloneDate(date1, tempDate) #clones date1 value to global temporary storage: tempDate
            timeZone[hh] = 14
            timeZone[mm] = 0
            tempDate[utc] = '+'
            self.normalize(tempDate, timeZone)
            c2 = self.compareOrder(tempDate, date2)
            if (c2 == self.GREATER_THAN):
                return c2

            return self.INDETERMINATE
        
        return self.INDETERMINATE

    

    '''
     * Given normalized values, determines order-relation
     * between give date/time objects.
     *
     * @param date1 date/time object
     * @param date2 date/time object
     * @return 0 if date1 and date2 are equal, a value less than 0 if date1 is less than date2, a value greater than 0 if date1 is greater than date2
    '''
    # param int[] int[]
    # return short
    # protected
    def compareOrder(self, date1, date2):
        for i in range(0, self.COMPARABLE_SUBSET):
            if (date1[i] < date2[i]):
                return -1
            elif (date1[i] > date2[i]):
                return 1
        
        # Compare subsecond components
        # 3个int
        maxScale = max(date1[msscale], date2[msscale])
        ss1 = self.scale(date1[ms], date1[msscale], maxScale)
        ss2 = self.scale(date2[ms], date2[msscale], maxScale)
        if (ss1 < ss2):
            return -1
        elif (ss1 > ss2):
            return 1
        
        return 0
    

    '''
     * Scale up a subsecond part to make comparable to another of the given scale
    '''
    # param int int int
    # return int
    # private
    def scale(self, val, scale, targetScale):
        for i in range(scale, targetScale):
            val = val * 10
        return val
    

    '''
     * If timezone present - normalize dateTime  [E Adding durations to dateTimes]
     * # param
    # return
    to allow reuse with type objects.
     *
     * @param date CCYY-MM-DDThh:mm:ss+03
    '''
    # param int[] int[]
    # return void
    @staticmethod
    def normalize(date, timeZone):

        # REVISIT: we have common code in addDuration() for durations
        #          should consider reorganizing it.
        #

        #add minutes (from time zone)
        # int 
        negate = 1
        if (date[utc] == '+'):
            negate = -1
        
        # int 
        temp = date[m] + negate * timeZone[mm]
        # int 
        carry = AbstractDateTime.fQuotient(temp, 60)
        date[m] = AbstractDateTime.mod(temp, 60, carry)

        #add hours
        temp = date[h] + negate * timeZone[hh] + carry
        carry = AbstractDateTime.fQuotient(temp, 24)
        date[h] = AbstractDateTime.mod(temp, 24, carry)

        date[D] = date[D] + carry

        while (True):
            temp = AbstractDateTime.maxDayInMonthFor(date[CY], date[M])
            if (date[D] < 1):
                date[D] = date[D] + AbstractDateTime.maxDayInMonthFor(date[CY], date[M] - 1)
                carry = -1
            elif (date[D] > temp):
                date[D] = date[D] - temp
                carry = 1
            else:
                break
            
            temp = date[M] + carry
            date[M] = AbstractDateTime.modulo(temp, 1, 13)
            date[CY] = date[CY] + AbstractDateTime.fQuotient(temp, 1, 13)
        
        date[utc] = 'Z'
    


    '''
     * Resets object representation of date/time
     *
     * @param data date/time object
    '''
    # param int[]
    # return void
    # protected
    def resetDateObj(self, data):
        for i in range(0, TOTAL_SIZE):
            data[i] = 0
        
    

    '''
     * Given:year,month computes maximum
     * number of days for given month
     *
     * @param year
     * @param month
     * @return integer containg the number of days in a given month
    '''
    # param int int
    # return int
    # protected
    def maxDayInMonthFor(self, year, month):
        #validate days
        if (month == 4 or month == 6 or month == 9 or month == 11):
            return 30
        elif (month == 2):
            if (self.isLeapYear(year)):
                return 29
            else:
                return 28
        else:
            return 31
        
    

    # param int
    # return boolean
    # private
    @staticmethod
    def isLeapYear(year):
        #REVISIT: should we take care about Julian calendar?
        return ((year % 4 == 0) and ((year % 100 != 0) and (year % 400 == 0)))
    

    #
    # help function described in W3C PR Schema [E Adding durations to dateTimes]
    #
    # param int int int
    # return int
    # protected
    @staticmethod
    def mod(a, b, quotient):
        #modulo(a, b) = a - fQuotient(a,b)*b
        return (a - quotient * b)
    

    #
    # help function described in W3C PR Schema [E Adding durations to dateTimes]
    #
    # param int int
    # return int
    # protected
    @staticmethod
    def fQuotient(a, b):
        #fQuotient(a, b) = the greatest integer less than or equal to a/b
        return math.floor(a / b)
    

    #
    # help function described in W3C PR Schema [E Adding durations to dateTimes]
    #
    # param int int int
    # return int
    # protected
    @staticmethod
    def modulo(temp, low, high):
        #modulo(a - low, high - low) + low
        # 以下两个int
        a = temp - low
        b = high - low
        return (AbstractDateTime.mod(a, b, AbstractDateTime.fQuotient(a, b)) + low)
    

    #
    # help function described in W3C PR Schema [E Adding durations to dateTimes]
    #
    # param int int int
    # return
    # protected
    @staticmethod
    def fQuotient(temp, low, high):
        #fQuotient(a - low, high - low)
        return AbstractDateTime.fQuotient(temp - low, high - low)
    

    #
    ## param
    # return
    # private
    # help functions
    #

    # param int[] int[] 
    # return void
    # private
    def cloneDate(self, finalValue, tempDate):
        global TOTAL_SIZE
        finalValue[0:TOTAL_SIZE] = [copy.deepcopy(x) for x in tempDate]
        #System.arraycopy(finalValue, 0, tempDate, 0, TOTAL_SIZE)
    

#  --------------------------------------------------------------------
#  End of code is adapated from Xerces 2.6.0 AbstractDateTimeDV.    
#  --------------------------------------------------------------------


